#!/bin/bash



#帮助
function usage()
{
  echo "Usage examples:"
  echo "$0 --repo2git <git_code>"
  echo "$0 --format <tag1> <tag2>"
  echo "$0 --apply <git_code>"
  echo "$0 --list <tag1> <tag2>"
  echo "$0 --checkTag <tag>"
  echo "$0 --dos2unix <path>"
  echo "$0 --patch <commit-id>"
  echo "$0 --init"
  echo "$0 --tag "
  echo "$0 --modify"

  exit 0
}
function usage_detail(){
  echo "Usage detail:"
  echo "#repo代码仓库转为大git仓库"
  echo "$0 --repo2git <git_code>"
  echo "#生成两个tag之间的补丁"
  echo "$0 --format <tag1> <tag2>"
  echo "#将补丁打入指定的git大仓库"
  echo "$0 --apply <git_code>"
  echo "#列出两个tag之间的提交"
  echo "$0 --list <tag1> <tag2>"
  echo "#校验repo所有仓库某个tag是否存在"
  echo "$0 --checkTag <tag>"
  echo "#指定类型文件转换成unix格式"
  echo "$0 --dos2unix <path>"
  echo "#初始化repo仓库的第一版"
  echo "$0 --init"
  echo "#根据commit-id号码生成大git仓库类型补丁"
  echo "$0 --patch <commit-id>"
  echo "#xxxx还没想到"
  echo "$0 --tag "
  echo "#修改repo生成的补丁,适应打入git"
  echo "$0 --modify"

  exit 0

}

function conflict_help(){
printf "两种解决冲突的方法:
1.解决冲突1
  强制合入git apply PATCH --reject
  根据生成的.rej文件合入冲突地方.edit edit edit修复冲突
  译注：根据.rej文件手动解决所有冲突）
  git add FIXED_FILES
  git am --resolved
  然后mv xxx.patch xxx.patch-fail
  然后apply继续打补丁

2.解决冲突2
  出现冲突后,用git apply --check检查下哪些文件导致无法打入patch,然后找出解决方案
  一般都是由于文件类型问题,check后
  然后fromdos这些文件
  然后git add -A
  然后am --resolved
  然后mv xxx.patch xxx.patch-fail
  然后apply继续打补丁
  git apply 0001-add-line.patch --check
  fromdos xxxfile
  git apply 0001-add-line.patch
  git add -A
  git am --resolved
  在解决完冲突以后,比如用git add来让git知道你已经解决完冲突了.
"
exit 1
}
#格式化输出
RED="\033[0;31m"
YELLOW="\033[1;33m"
GREEN="\033[0;32m"
NO_COLOR="\033[0m"
BOLD="\033[1m"
UNDERLINE="\033[4m"
warn_debug=true
die() {
    echo -e "==> ${RED}${@}${NO_COLOR}"
    exit 1
}

warn() {
	if [[ "$warn_debug" = true ]]; then
    	echo -e "==> ${YELLOW}${@}${NO_COLOR}"
	fi
}

good() {
    echo -e "==> ${GREEN}${*}${NO_COLOR}"
}

function showTime(){
	currentTime=$(date +%s)
	echo $(date)
	return $currentTime
}
function isRepo(){
	if [ ! -d "./.repo" ];then
	  echo "This command must be executed in repo directory."
	  exit -1
	fi
}
#转换文本dos格式
function change_from_dos(){
	local ENCRYPT_FILE_TYPE="README|NOTICE|.*\.mt6750$\|.*\.custom$\|.*\.drivers$\|.*\.rc$\|.*\.patch$\|Makefile$\|makefile\|.*\.sh$|.*\.mk$\|.*\.xml$\|.*\.java$\|.*\.[cC]$\|.*\.cpp$\|.*\.h$\|.*\.hpp$\|.*\.[sS]$\|.*\.cc$"
	local path=$1
	for dfile in `find ${path} -regex "$ENCRYPT_FILE_TYPE"`
	do
		fromdos $dfile
		echo $dfile
	done
	unset dfile
	unset path
}

#获取android代码的主目录
function gettop
{
	local TOPFILE=build/core/envsetup.mk
	if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
		# The following circumlocution ensures we remove symlinks from TOP.
		(cd $TOP; PWD= /bin/pwd)
	else
		if [ -f $TOPFILE ] ; then
			# The following circumlocution (repeated below as well) ensures
			# that we record the true directory name and not one that is
			# faked up with symlink names.
			PWD= /bin/pwd
		else
			local HERE=$PWD
			T=
			while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
				\cd ..
				T=`PWD= /bin/pwd -P`
			done
			\cd $HERE
			if [ -f "$T/$TOPFILE" ]; then
				echo $T
			fi
		fi
	fi
}


function getGit()
{
	TOPFILE=build/core/envsetup.mk
	local HERE=$PWD
	T=
	while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
		T=$PWD
		if [ -d "$T/.git" ]; then
			echo ${T}
			return
		fi
		cd ..
	done
	cd ${HERE}
	#echo "can't find .git"
}





#判断tag是否缺失
function is_lose_tag(){
	local tag=$1
	for project in $(cat .repo/project.list)
	do
		cd $project
		pwd
		hasTag $tag
		good $tag exist
		cd - >/dev/null
	done
	unset project

}
#根据commit设置tag
function set_tag_of_commit(){
	#local TAG_NAME=$1
	#local TAG_TIME=$(date +%Y-%m-%d" "%T)
	#local commit_id=$2
	local date=$1
	local project
	for project in $(cat .repo/project.list)
	do
		cd $project
		echo $(pwd)
		commit_id=$(git log --reverse --oneline --pretty=%h | head -2 | tail -1)
		#git tag -a "$TAG_NAME" -m "$TAG_TIME" $commit_id
		git tag -a "ali_base_${date}" -m "${date}" $commit_id
		cd - > /dev/null
	done
	unset project
}
#获取tag对应的commit
function get_commit_from_tag(){
    local tag_name=$1
	for project in $(cat .repo/project.list)
    do
        cd $project
        echo -n $project"|"
        commit_id=$(git log -1 --oneline $tag_name --format=%H)
        #commit_id=$(git log -1 --oneline $tag_name --format=%H-%s)
        echo $commit_id
        cd - > /dev/null
    done
}
#根据文件设置tag
function set_tag(){
	local file=$2
	local tag_name=$1
	local TAG_TIME=$(date +%Y-%m-%d" "%T)
	for content in $(cat $file)
	do
		project_path=$(echo $content| awk -F"|" '{print $1}')
		project_commit=$(echo $content| awk -F"|" '{print $2}')
		echo $project_path
		echo $project_commit
		cd $project_path
		git tag -a "$tag_name" -m "$TAG_TIME" $project_commit
		cd - > /dev/null
	done



}
#判断git是否存在
function hasTag(){
	local tag=$1
	if [ -z "`git tag -l | grep -w ${tag}`" ];then
		die "未找到$tag"
		return 1
	else
		return 0
	fi
	
}
#对比两个tag之间的差异
function get_tag_diff_list(){
	local before_tag_name=$1
	local after_tag_name=$2
	for project in $(cat .repo/project.list)
	do
		#echo $project
		cd $project
		hasTag $before_tag_name
		hasTag $after_tag_name
		commit_list=$(git log --oneline ${before_tag_name}..${after_tag_name})
		if [ -n "$commit_list" ];then
			echo PATH: $project
			echo COMMIT:
			echo "$commit_list"
			echo 
		fi
		cd - > /dev/null
	done



}
#第一次设置大git仓库
function repo2git_init(){
	local project
	for project in $(cat .repo/project.list)
	do
		#cd到每个仓库下面
		cd $project
		echo $(pwd)
		#找到初始化时候的commit id
		init_commit_id=$(git log --reverse --oneline --pretty=%h | head -2 | tail -1)
		#将代码checkout到阿里初始化的状态
		git checkout $init_commit_id 1>/dev/null 2>/dev/null
		#判断当前checkout是否成功，如果成功删除当前.git仓库
		if [ $(git log --oneline | wc -l) -le 2 ];then
			echo "git checkout sucess"
			if [ -d .git ];then
				echo "find .git dir!"
				#rm -rfv .git
			fi
		fi
		cd - > /dev/null
	done
	unset project
}

#修改补丁（旧方法修改patch的表头，弃用）
function modify_format_patch(){
	cd $OUT_DIR
	for patch_path in $(find . -name "*.patch" -type f)
	do
		cd $(dirname $patch_path)
		patch=$(basename $patch_path)
		cur_dir=$(pwd)
		add_path=${cur_dir:${#OUT_DIR}+1}
		echo
		good "当前路径:$cur_dir"
		good "修改补丁文件:$patch"
		good "该补丁文件大小:$(du -sh $patch | awk '{print $1}') "
		good "准备修改路径:$add_path"
		du -sh $patch | awk -F" " '{if($1~/M/)print "==> 补丁文件较大,请稍等..."}'
		test -s $patch || mv $patch ${patch}-error
		#warn "补丁文件可能过于大,耗时比较长,请稍等....."
		#修改表头
		sed -i "/^diff --git/{s#\ba/#a/${add_path}/# ; s#\bb/#b/${add_path}/#}" $patch
		sed -i "/^--- a/{s#---\ a/#---\ a/${add_path}/#}" $patch
		sed -i "/^+++ b/{s#+++\ b/#+++\ b/${add_path}/#}" $patch
		good "修改补丁文件完成!"
		cd $OUT_DIR
	done
	cd $CUR_DIR

}
#打入补丁
function git_apply_patch(){
	local git_code=$1
	local cur_dir=$(pwd)
	cd $git_code
	for patch in $(find $OUT_DIR -name "*.patch" -type f | sort )
	do 
		fromdos $patch
		good "Applying $patch"
		#git am $patch --whitespace=nowarn  
		#git apply --check $patch
		#由于commit-id的改变导致-3-way方式还是已经合入的patch无法检测出来
		#git revert 反打的patch无法合入...有点蛋疼
		#git am  -3 -k $patch
		git am  -3  $patch
		result=$?
		echo "$git_code result=$?"
		#.rej 文件手动对比合入
		if [ ${result} = 1 ];then
			warn "冲突:${git_code}/xxxxx路径下解决冲突后,git am --resolved"
			conflict_help
		else
			mv $patch $patch-sucess
		fi
		echo $patch >> $OUT_DIR/patch_list
	done
	mv -v $OUT_DIR ${OUT_DIR}_${THIS_TAG}
	test -d updatelist || mkdir updatelist
	cp -fv ${OUT_DIR}_${THIS_TAG}/update_list.txt updatelist/${THIS_TAG}.txt
	git add updatelist
	git commit -m "common:add updatelist ${THIS_TAG}"
	good "Patch已经完成,请记得在$git_code打上tag"
	good "请确认完成后到${git_code}下执行git tag -a ${THIS_TAG} -m ${DATE}"

	unset patch
	cd $cur_dir

}
#单步生成标准补丁文件，用于适用单步提取补丁
function format_commit_patch(){
	local T=$(gettop)
	local gitDir=$(getGit)
	local commit=$1
	good ${T}
	good ${gitDir}
	test -z "${gitDir}" && die "未找到.git仓库"
	local dir=${gitDir:${#T}+1}
	good "${dir}"
	git format-patch -1 --src-prefix="a/${dir}/" --dst-prefix="b/${dir}/"  ${commit} # -o $OUT_DIR/$dir

}

#批量生成标准补丁文件，并且将补丁格式改变为适用git大仓库
function format_tag_patch(){
	local last_tag=$1
	local this_tag=$2
	good "删除输出路径,重新创建"
	rm -rf $OUT_DIR
	mkdir -p $OUT_DIR
	good "正在生成repo仓库下补丁"
	local cur_dir=$(pwd)
	for dir in $(cat .repo/project.list)
	do
		cd $dir
		mkdir -p $OUT_DIR/$(dirname $dir)
		#准确匹配tag生成patch
		#git format-patch -k -p -b -w --src-prefix="a/${dir}/" --dst-prefix="b/${dir}/"  ${last_tag}..${this_tag} -o $OUT_DIR/$dir
		git format-patch  --src-prefix="a/${dir}/" --dst-prefix="b/${dir}/"  ${last_tag}..${this_tag} -o $OUT_DIR/$dir
		#git format-patch ${last_tag}..${this_tag} -o $OUT_DIR/$dir
		#git format-patch -s $tag_name -o $OUT_DIR/$dir
		cd $cur_dir
	done
	unset dir
	unset cur_dir
}

#主函数入口
function repo2git(){
	local git_code="$1"
	pushd . > /dev/null
	cd $git_code
	last_tag=$(git tag -l | tail -1 )
	popd > /dev/null
	isRepo
	test -z $last_tag && die "没有获取到上一版的tag标志"
	showTime
	startTime=$?
	good "CURRENT_DIR = $CUR_DIR"
	#生成补丁
	format_tag_patch $last_tag $THIS_TAG
	good "总共生成了$(find $OUT_DIR -name "*.patch" -type f| wc -l)个patch文件"
	good "总共生成了$(du -sh $OUT_DIR | awk '{print $1}' )"
	#read -p "是否需要修改补丁内容,点击enter继续(取消请按ctrl+c)...."
	#修改补丁内容,适配路径
	#modify_format_patch
	good "修改完成!"
	#生成更新文件列表
	get_tag_diff_list ${last_tag} ${THIS_TAG} 2>&1 | tee ${OUT_DIR}/update_list.txt
	#将补丁打入git大仓库
	read -p "是否需要将补丁打入仓库,点击enter继续(取消请按ctrl+c)...."
	#打入补丁
	git_apply_patch $git_code
	showTime
	endTime=$?
	countTime=$(echo "$endTime-$startTime" |bc)
	good "耗时:$countTime秒"
}
#同步更新repo代码
function repo_sync_code(){
	isRepo
	#同步代码
	repo sync -c 
	#强制更新两次，避免出错导致代码提交结束，没有更新完成
	repo sync -c
	#判断成功
	local reply
	read -p "是否已经更新完成,确认后打入tag:[y/n]" reply
	if [ "${reply}" = n ];then
		good "repo forall -c git tag -a ${THIS_TAG} -m ${DATE}"
		die "更新未成功."
	fi
	#打上今天日期的tag
	repo forall -c git tag -a "${THIS_TAG}" -m "${DATE}"
    #check tag是否完成
	is_lose_tag ${THIS_TAG}

}

#参数获取区域
###################################################[main]###########################################################################################
ARGS=$(getopt -o b:h -al repo2git,init,sync,dos2unix,tag,format,patch,modify,apply,list,checkTag,test,help -- "$@")

[ $? -ne 0 ] && usage

eval set -- "${ARGS}" 
#echo "caicai_debug: $@"
while true  ;do
        case "$1" in
        --repo2git) vRepo2git="yes";;
		#初始化第一次生成大git
        --init) vInit="yes";;
		#转码
        --dos2unix) vDos2unix="yes";;
        #同步代码
		--sync) vSync="yes";;
		#head or tail   ##git push tag##
        --tag) vTag="yes";;
        #检查tag是否打全
        --checkTag) vcheckTag="yes";;
		#生成补丁
        --format) vFormat="yes";;
		#修改指定位置补丁
        --patch) vPatch="yes";;
        --modify) vModify="yes";;
		#打入指定位置的补丁到指定位置
        --apply) vApply="yes";;
        #查看两个tag之间的提交
        --list) vList="yes";;
        #内调函数
        --test) vTest="yes";;
        -b) branch="$2";shift;;
        -h|--help) usage_detail;;
        --) arg1="$2"; arg2="$3"; arg3="$4"; break;;
        esac
shift
done
#############################################################
#初始化变量
CUR_DIR=$(pwd)
OUT_DIR="$CUR_DIR/out_patch"
DATE=$(date +%Y%m%d)
THIS_TAG="ali_base_${DATE}"
#THIS_TAG="ali_base_20160531"
#############################################################
if [ "$vRepo2git" == "yes" -a -n "$arg1" ];then
	#repo2git "$arg1" 2>&1 | tee ${OUT_DIR}/repo2git_log_${DATE}.txt  #OUT目录还未生成
	repo2git "$arg1" 
	exit 0
fi
if [ "$vSync" == "yes" ];then
	repo_sync_code
	exit 0
fi
if [ "$vInit" == "yes" ];then
	repo2git_init
	exit 0
fi
if [ "$vDos2unix" == "yes" -a -n "$arg1"  ];then
	change_from_dos "$arg1"
	exit 0
fi
if [ "$vTag" == "yes" -a -n "$arg1"  ];then
	repo2git_tag "$arg1"
	exit 0
fi
if [ "$vFormat" == "yes" -a -n "$arg1"  ];then
	if [ -z "$arg2" ];then 
		arg2=$THIS_TAG
	fi
	format_tag_patch "$arg1" "$arg2"
	exit 0
fi
if [ "$vPatch" == "yes" -a -n "$arg1"  ];then
	format_commit_patch "$arg1"
	exit 0
fi
if [ "$vModify" == "yes" ];then
	modify_format_patch
	exit 0
fi
if [ "$vApply" == "yes" -a -n "$arg1"  ];then
	git_apply_patch "$arg1"
	exit 0
fi
if [ "$vcheckTag" == "yes" -a -n "$arg1"  ];then
	is_lose_tag "$arg1"
	exit 0
fi
if [ "$vTest" == "yes"  ];then
	good "执行内调函数$@"
	$arg1 $arg2 $arg3
	exit 0
fi
if [ "$vList" == "yes" -a -n "$arg1"  ];then
	if [ -z "$arg2" ];then 
		arg2=$THIS_TAG
	fi
	get_tag_diff_list "$arg1" "$arg2"
	exit 0
fi
#repo仓库 tag的转移
#get_commit_from_tag $tag > $file
#set_tag $THIS_TAG $file

usage
#######################################################################################################################################################
#1.流程:
#1.同步代码,打上当日同步日期的tag
#2.获取这次版本更新内容,并且保留列表,方便查看
#3.获取git仓库最后更新的内容,选择性更新repo的内容




#out_patch:
#1.两个tag之间的patch详情 get_tag_diff_list
#2.patch打入情况标识 project.list 和 success fail标识
#3.patch大小,个数统计,时间 
#4.patch对应的commit标识 
#5.repo的tag对应的commit_id list
#打完后保存out_patch为tag名称out_patch,以便后续查看

